/*
 * Copyright (C) 2011, EADS France
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 */

package xowl.store.loaders;

import java.util.HashMap;
import java.util.Map;
import xowl.lang.owl2.AnonymousIndividual;
import xowl.lang.owl2.IRI;
import xowl.lang.owl2.Ontology;
import xowl.store.rdf.RDFAnonymousNode;
import xowl.store.rdf.RDFBlankNode;
import xowl.store.rdf.RDFGraph;
import xowl.store.rdf.RDFNode;
import xowl.store.rdf.RDFProperty;
import xowl.store.rdf.RDFSubjectNode;
import xowl.store.voc.OWLDatatype;
import xowl.utils.Logger;

/**
 *
 * @author Laurent WOUTERS
 */
public class NTriplesLoader implements Loader {
    private static final String component = "N-Triples Loader";
    
    private static final String defaultRDFGraphs = "http://www.xowl.org/interpreter/rdfgraphs/";
    
    private static final SymbolTerminal[] terminals = {
            new SymbolTerminal(0x1, "ε"),
            new SymbolTerminal(0x2, "$"),
            new SymbolTerminal(0x9, "PN_LOCAL"),
            new SymbolTerminal(0x10, "DOT"),
            new SymbolTerminal(0x5, "BLANK"),
            new SymbolTerminal(0x8, "PN_CHARS"),
            new SymbolTerminal(0xF, "LIT_NUM"),
            new SymbolTerminal(0xA, "FULLIRI"),
            new SymbolTerminal(0xC, "LIT_STRING"),
            new SymbolTerminal(0xB, "NODEID"),
            new SymbolTerminal(0xE, "LIT_LING"),
            new SymbolTerminal(0xD, "LIT_TYPED")
    };
    
    private RDFGraph graph;
    private Ontology ontology;
    private Map<String, RDFBlankNode> blanks;
    
    public Ontology getOntology() { return ontology; }
    
    public NTriplesLoader(xowl.store.rdf.RDFGraph graph) {
        this.graph = graph;
        this.blanks = new HashMap<String, RDFBlankNode>();
    }
    
    @Override public void load(Logger logger, String name, java.io.Reader reader) {
        java.util.Random rand = new java.util.Random();
        String value = defaultRDFGraphs + Integer.toHexString(rand.nextInt());
        IRI iri = new IRI();
        iri.setHasValue(value);
        ontology = new Ontology();
        ontology.setHasIRI(iri);
        RewindableStream stream = new RewindableStream(reader);
        Lexer lexer = new Lexer(Automaton.load("xowl/store/loaders/NTriplesLexer.bin"), terminals, stream, terminals[4]);
        int count = 0;
        int bar = 0;
        while (true) {
            RDFNode n1 = getNode(lexer);
            if (n1 == null)
                break;
            RDFNode n2 = getNode(lexer);
            RDFNode n3 = getNode(lexer);
            String dot = lexer.getNext();
            if (lexer.getMatchedTerminal().getSID() != 0x10) {
                System.out.println("oops");
            }
            graph.add(ontology, (RDFSubjectNode)n1, (RDFProperty)n2, n3);
            count++;
            bar++;
            if (bar == 100000) {
                logger.debug(component, Integer.toString(count));
                bar = 0;
            }
        }
        logger.debug(component, Integer.toString(count));
    }
    
    private RDFNode getNode(Lexer lexer) {
        String value = lexer.getNext();
        int sid = lexer.getMatchedTerminal().getSID();
        if (sid == 0xA)
            return graph.getNodeIRI(value.substring(1, value.length() - 1));
        else if (sid == 0xB) {
            String id = value.substring(2);
            RDFBlankNode node = blanks.get(id);
            if (node != null)
                return node;
            node = graph.getBlankNode();
            blanks.put(id, node);
            return node;
        } else if (sid == 0xC) {
            return graph.getNodeLiteral(unescape(value), OWLDatatype.xsdString);
        } else if (sid == 0xD) {
            int index = value.lastIndexOf("^^");
            return graph.getNodeLiteral(unescape(value.substring(0, index+1)), value.substring(index + 3, value.length()-1));
        } else if (sid == 0xE) {
            int index = value.lastIndexOf("@");
            return graph.getNodeLiteral(unescape(value.substring(0, index+1)), OWLDatatype.xsdString);
        } else if (sid == 0xF) {
            if (value.contains("."))
                return graph.getNodeLiteral(value, OWLDatatype.xsdFloat);
            else
                return graph.getNodeLiteral(value, OWLDatatype.xsdInteger);
        }
        return null;
    }
    
    private String unescape(String content) {
        char[] buffer = new char[content.length()-2];
        int next = 0;
        for (int i=1; i!=content.length()-1; i++) {
            char c = content.charAt(i);
            if (c != '\\') {
                buffer[next++] = c;
            } else {
                char n = content.charAt(i+1);
                if (n == 't') { buffer[next++] = '\t'; i++; }
                else if (n == 'n') { buffer[next++] = '\n'; i++; }
                else if (n == 'r') { buffer[next++] = '\r'; i++; }
                else if (n == 'r') { buffer[next++] = '\r'; i++; }
                else if (n == '\\') { buffer[next++] = '\\'; i++; }
                else if (n == '"') { buffer[next++] = '"'; i++; }
                else if (n == 'u') {
                    int codepoint = Integer.parseInt(content.substring(i+2, i+6), 16);
                    String str = new String(new int[] {codepoint}, 0, 1);
                    for (int j=0; j!=str.length(); j++)
                        buffer[next++] = str.charAt(j);
                    i += 5;
                } else if (n == 'U') {
                    int codepoint = Integer.parseInt(content.substring(i+2, i+10), 16);
                    String str = new String(new int[] {codepoint}, 0, 1);
                    for (int j=0; j!=str.length(); j++)
                        buffer[next++] = str.charAt(j);
                    i += 9;
                }
            }
        }
        return new String(buffer, 0, next-1);
    }
}